#include "SStruct.h"

#include "ExpressionResolutor.h"

void SStruct::Set(string key, SValueSp value)
{
	// The key must be defined
	if ( !typeDescriptor->IsDefined(key) )
	{
		throw SArgumentException("There is not a member with the name '" + key + "'");
	}
	if ( !value )
	{
		throw SArgumentException("The value cannot be null.");
	}

	// The member type must be the same of value

	SStructMemberSp member = typeDescriptor->GetMember(key);

	if ( member->GetType() != value->GetType() )
	{
		throw SArgumentException("The value is the incorrect type.");
	}
	if ( value->IsComplexType() )
	{
		// The member type descriptor must be the same if the value is complex
		// Attention: type descriptors must be smart pointer to the same object !!!

		//if ( value->As<SComplexValue>()->GetTypeDescriptor() != member->GetTypeDescriptor() )
		//{
		//	throw SArgumentException("Value is of the same type but with a different type descriptor");
		//}

		if ( value->GetTypeName() != member->GetTypeDescriptor()->GetFullyQualifiedName() )
		{
			throw SArgumentException("Value is of the same type but with a different type descriptor");
		}
	}

	// Add the value 
	values[key] = value;
}

void SStruct::Set( int index, SValueSp value )
{
	string key = typeDescriptor->GetMember(index)->GetName();
	Set(key, value);			

}

SValueSp SStruct::Get(string key)
{
	if ( !typeDescriptor->IsDefined(key) )
	{
		throw SArgumentException("There is not a member with the name '" + key + "'");
	}
	if ( !IsSet(key) )
	{
		throw SArgumentException("The member '" + key + "' is not set.");
	}

	return values[key];
}

SValueSp SStruct::Get( int index )
{
	string key = typeDescriptor->GetMember(index)->GetName();
	return Get(key);
}

string SStruct::ToString(string format)
{
	ostringstream os;

	if ( format == "pp" )
	{
		os << "{" << endl << "\t";
	}
	else
	{
		os << "{ ";
	}

	int len = typeDescriptor->GetNumberOfMembers();
	for(int i=0; i<len; ++i)
	{
		SStructMemberSp entry = typeDescriptor->GetMember(i);
		string key = entry->GetName();

		// Show only the member with a value
		if ( values[key] )
		{
			if ( i > 0)
			{
				if ( format == "pp" )
				{
					// Each member is on a different line
					os << "," << endl << "\t";
				}
				else
				{
					// All the members are on the same line
					os << ", ";
				}
			}

			if ( format == "pp")
			{
				os << key << " = " << Utility::GetPrettyPrintVar(values[key]);
			}
			else
			{
				os << key << " = " << values[key]->ToString();
			}
		}
	}
   
	if ( format == "pp")
	{
		os << endl << "}";
	}
	else
	{
		os << " }";
	}

	return os.str();
}

SStructSp SStruct::Parse(SStructTypeDescriptorSp typeDescriptor, string s)
{
	SValueSpMapSp vars(new SValueSpMap);

	return Parse(typeDescriptor, s, vars);
}

SStructSp SStruct::Parse(SStructTypeDescriptorSp typeDescriptor, string s, SValueSpMapSp vars)
{
	if (!typeDescriptor || (typeDescriptor && !typeDescriptor->IsValid()))
	{
		throw SArgumentException("The struct type descriptor is not valid.");
	}

	trim(s);

	smatch m;
	SStructSp str = SStruct::Create(typeDescriptor);

	// If the string is an empty struct { }
	if ( regex_match( s, m, regex("\\{\\s*\\}") ) )
	{
		return str;
	}

	if ( !regex_match( s, m, regex("\\{(.*)\\}") ) )
	{
		throw SParseException("Syntax error: " + s + ". Struct must be put inside two brackets.");
	}
	
	string stringArgs = m[1];

	// Split arguments
	StringVector args;
	Utility::MergeArgs(stringArgs, args);

	// If there are too member raise a parse exception
	if ( (int) args.size() > typeDescriptor->GetNumberOfMembers() )
	{
		throw SParseException("Syntax error: " + s + ". Too parameters supplied.");
	}

	// Now in args there are all the single member
	bool namedParameterFound = false;

	try
	{
		for(unsigned int i=0; i<args.size(); ++i)  // I use the index so don't use BOOST_FOREACH
		{
			string arg = args[i];

			trim( arg );
			
			// Check if the argument is a named parameter

			if ( regex_match( arg, m, regex("([^=\\{]*)\\s*=\\s*(.*)") ) )
			{
				namedParameterFound = true;

				string parName = m[1];
				string value = m[2];

				trim(parName);
				trim(value);

				SStructMemberSp member = typeDescriptor->GetMember(parName);

				if (Utility::IsSimpleType(member->GetType()))
				{
					str->Set(parName, ExpressionResolutor::Resolve(value, vars, member->GetType()) );
				}
				else
				{
					str->Set(parName, ExpressionResolutor::Resolve(value, vars, member->GetTypeDescriptor()) );			
				}				
			}
			else // ... if the argument is not a named parameter
			{
				if ( namedParameterFound )
				{
					throw SParseException();
				}

				SStructMemberSp member = typeDescriptor->GetMember(i);

				if (Utility::IsSimpleType(member->GetType()))
				{
					str->Set(i,  ExpressionResolutor::Resolve(arg, vars, member->GetType()) );			
				}
				else
				{
					str->Set(i, ExpressionResolutor::Resolve(arg, vars, member->GetTypeDescriptor()) );			
				}							
			}
		}
	}
	catch(SException& )
	{
		throw SParseException("Syntax error: " + s + " is not a valid SStruct literal.");
	}

	return str;
}

bool SStruct::IsSet( string key )
{
	if ( !typeDescriptor->IsDefined(key) )
	{
		throw SArgumentException("A member with the name '" + key + "' is not defined.");
	}

	SValueSpMap::iterator it = values.find(key);
	if (it == values.end() )
	{
		return false;
	}

	return true;
}

bool SStruct::IsAllMemberSet()
{
	for(int i=0; i<GetNumberOfMembers(); ++i)
	{
		string key = typeDescriptor->GetMember(i)->GetName();

		if ( !IsSet(key) )
		{
			return false;
		}
	}

	return true;
}

SStructSp SStruct::Create( SStructTypeDescriptorSp typeDescriptor )
{
	if ( typeDescriptor && !typeDescriptor->IsValid() )
	{
		throw SArgumentException("The type descriptor is not valid.");
	}

	return SStructSp(new SStruct(typeDescriptor));
}

SValueSpMapSp SStruct::GetAllMembersAsMap()
{
	SValueSpMapSp vars(new SValueSpMap());

	for(int i=0; i<(int)GetNumberOfMembers(); ++i)
	{
		SStructMemberSp member = typeDescriptor->GetMember(i);
		string varName = member->GetName();
		SValueSp var = Get(i);

		(*vars)[varName] = var;
	}

	return vars;
}